package testApplets;

import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Vector;

import marchingCubes.DualCube;

import controlP5.ControlP5;
import controlP5.Range;
import controlP5.Slider;
import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PGraphics2D;
import processing.core.PGraphics3D;
import processing.core.PMatrix3D;
import simpleGeom.AbstractFace;
import simpleGeom.BasicMesh;
import simpleGeom.Bounds3D;
import simpleGeom.Box3D;
import simpleGeom.HasPos3D;
import simpleGeom.PCamera3D;
import simpleGeom.QuadFace;
import simpleGeom.Rect2D;
import simpleGeom.SimpleTriangle;
import simpleGeom.Vec3D;
import testApplets.MeshToSolid2.Cell;

public class MeshCutApplet extends PApplet {
	BasicMesh mesh;
	PCamera3D cam;
	ControlP5 cp5;
	Slider[] xSlider;
	Slider[] ySlider;
	Slider[] zSlider;
	
	float sX,sY,sZ;
	float dimCubeX = 0.32f;
	float dimCubeY = 0.32f;
	float dimCubeZ = 0.32f;
	float dimFineCubeX=0.02f;
	float dimFineCubeY=0.02f;
	float dimFineCubeZ=0.02f;
	int fac=16;
	Cell[][][] grid;
	Cell[][][] gridFine;
	Button[]xButs;
	Button[]yButs;
	Button[]zButs;
	Box3D bounds;
	//bounds: x1:-2.14084 y1:-2.22545 z1:-1.7528 dX:4.28168 dY:4.13662 dZ:4.46073
	
	Range  xRange,yRange,zRange; 
	Slider sliderX1;
	Slider sliderY1,sliderY2;
	Slider sliderZ1,sliderZ2;
	GridCanvas canvas;
	
	int x1;
	int x2;
	int y1;
	int y2;
	int z1;
	int z2;

	ButtonGroup bgroup;
	ButtonGroup groupType;
	Box3D innerBounds;
	Color[] colors=new Color[]{Color.CYAN,Color.GREEN,Color.RED,Color.BLUE,Color.YELLOW,Color.MAGENTA,Color.ORANGE,Color.DARK_GRAY};

	float iso=0.5f;

	boolean doReset;

	public void setup() {
		size(1200, 700, OPENGL);
		cam = new PCamera3D(this,0,0,100);
		mesh = new BasicMesh();
		loadDistance(this.selectInput());
		//mesh.load("/Users/caad/Desktop/archive/complete/testPiece.obj");
		//mesh.load("/Users/caad/Desktop/archive/complete/simple.obj");
		//gridFine=voxelize(dimFineCubeX,dimFineCubeY,dimFineCubeZ);
		createGUI();
		
		grid=new Cell[gridFine.length/fac][gridFine[0].length/fac][gridFine[0][0].length/fac];
		for (int x=0;x<grid.length;x++){
			for (int y=0;y<grid[0].length;y++){
				for (int z=0;z<grid[0][0].length;z++){
					Cell cell=new Cell(bounds.x1+x*dimCubeX,bounds.y1+y*dimCubeY,bounds.z1+z*dimCubeZ);
					cell.xK=(short)x;
					cell.yK=(short)y;
					cell.zK=(short)z;
					grid[x][y][z]=cell;
					int x1=x*fac;
					int y1=y*fac;
					int z1=z*fac;
					int x2=(x+1)*fac;
					int y2=(y+1)*fac;
					int z2=(z+1)*fac;
					for (int cX=x1;cX<x2;cX++){
						for (int cY=y1;cY<y2;cY++){
							for (int cZ=z1;cZ<z2;cZ++){
								Cell fCell=gridFine[cX][cY][cZ];
								if (fCell.getValue()>0){
									grid[x][y][z].setValue(1);
									cX=x2;
									cY=y2;
									cZ=z2;
								}
							}
						}
					}
				}
			}
		}
		findOutline(gridFine,gridFine.length/2,gridFine[0].length/2,gridFine[0][0].length/2);
		createButtons();
		//saveDistance(this.selectOutput());
		reset();
		//exportFaces(this.selectOutput(),gridFine);
	}

	public void createButtons(){
		int nX=grid.length;
		int nY=grid[0].length;
		int nZ=grid[0][0].length;
		int bDim=10;
		int startB=200;
		int startX=20;
		xButs=new Button[nX];
		for (int i=0;i<nX;i++){
			Button bt=new Button(startX+i*bDim,startB,startX+(i+1)*bDim,startB+bDim);
			this.addMouseListener(bt);
			xButs[i]=bt;
			bt.active=true;
		}
		startB=210;
		yButs=new Button[nY];
		for (int i=0;i<nY;i++){
			Button bt=new Button(startX+i*bDim,startB,startX+(i+1)*bDim,startB+bDim);
			this.addMouseListener(bt);
			yButs[i]=bt;
		}
		startB=220;
		zButs=new Button[nZ];
		for (int i=0;i<nZ;i++){
			Button bt=new Button(startX+i*bDim,startB,startX+(i+1)*bDim,startB+bDim);
			this.addMouseListener(bt);
			zButs[i]=bt;
			bt.active=true;
		}
		startB=height-40;
		bgroup=new ButtonGroup();
		for (int i=0;i<nY;i++){
			Button bt=new Button(startX+i*bDim,startB,startX+(i+1)*bDim,startB+bDim);
			this.addMouseListener(bt);
			bgroup.add(bt);
			bgroup.radio=true;
		}
		startB=height-20;
		groupType=new ButtonGroup();
		for (int i=0;i<8;i++){
			Button bt=new Button(startX+i*bDim,startB,startX+(i+1)*bDim,startB+bDim);
			bt.color=colors[i];
			this.addMouseListener(bt);
			groupType.add(bt);
			groupType.radio=true;
		}
		canvas=new GridCanvas(20,400,10);
		this.addMouseMotionListener(canvas);
	}
	public void createGUI() {
		cp5 = new ControlP5(this);
		//.setRange(bounds.x2,bounds.x2)
		xRange=cp5.addRange("xRange").setPosition(20,50)
				 .setBroadcast(false)
	             .setSize(200,20)
	             .setHandleSize(20)
	             .setRange(-2.24f,2.24f)
	             .setRangeValues(-1.95f,1.95f)
	             .setBroadcast(true) ;
		//xRange.snapToTickMarks(false);
		//xRange.s
		//xRange.setLowValue(-1.95f);
		println("lowValue: "+xRange.getLowValue());
		yRange=cp5.addRange("yRange").setPosition(20,80)
				 .setBroadcast(false)
	             .setSize(200,20)
	             .setHandleSize(20)
	             .setRange(bounds.y1,bounds.y2)
	             .setRangeValues(-1.72f,1.3f)
	             .setBroadcast(true) ;
		zRange=cp5.addRange("zRange").setPosition(20,110)
				 .setBroadcast(false)
	             .setSize(200,20)
	             .setHandleSize(20)
	             .setRange(bounds.z1,bounds.z2)
	             .setRangeValues(-0.9f,2.5f)
	             .setBroadcast(true) ;
		/*sliderX1=cp5.addSlider("x1").setPosition(20,20 ).setRange(bounds.x1, bounds.x2).setSize(200, 20).setValue(bounds.x1);
		//sliderX2=cp5.addSlider("x2").setPosition(20, 40).setRange(bounds.x1, bounds.x2) .setSize(200, 20).setValue(bounds.x2);
		
		sliderY1=cp5.addSlider("y1").setPosition(20,40).setRange(bounds.y1, bounds.y2) .setSize(200, 20).setValue(bounds.y1);
		sliderY2=cp5.addSlider("y2").setPosition(20, 60).setRange(bounds.y1, bounds.y2) .setSize(200, 20).setValue(bounds.y2);  
		
		sliderZ1=cp5.addSlider("z1").setPosition(20, 80).setRange(bounds.z1, bounds.z2) .setSize(200, 20).setValue(bounds.z1);
		sliderZ2=cp5.addSlider("z2").setPosition(20, 100).setRange(bounds.z1, bounds.z2) .setSize(200, 20).setValue(bounds.z2); */
	
		cp5.setAutoDraw(false);
	}
	
	public void setOff(Cell[][][] grid){
		Cell vx=grid[0][0][0];
		float dimCubeX=grid[1][0][0].x-vx.x;
		float dimCubeY=grid[0][1][0].y-vx.y;
		float dimCubeZ=grid[0][0][1].z-vx.z;
		float sX=vx.x;
		float sY=vx.y;
		float sZ=vx.z;
		float d=xRange.getLowValue()-bounds.x1;
		xRange.setHighValue(bounds.x2-d);
		 x1=(int)((xRange.getLowValue()-sX)/dimCubeX);
		 x2=(int)((xRange.getHighValue()-sX)/dimCubeX);
		 y1=(int)((yRange.getLowValue()-sY)/dimCubeY);
		 y2=(int)((yRange.getHighValue()-sY)/dimCubeY);
		 z1=(int)((zRange.getLowValue()-sZ)/dimCubeZ);
		 z2=(int)((zRange.getHighValue()-sZ)/dimCubeZ);
		 
		for (int x=0;x<grid.length;x++){
			for (int y=0;y<grid[0].length;y++){
				for (int z=0;z<grid[0][0].length;z++){
					Cell v=grid[x][y][z];
					if (x<x1||x>x2||y<y1||y>y2||z<z1||z>z2){
						v.off=true;
						
					}
					else{
						v.off=false;
					}
				}
			}
		}
	}
	public void reset(){
		//setOff(grid);
		setOff(gridFine);
		findOutline(gridFine,gridFine.length/2,gridFine[0].length/2,gridFine[0][0].length/2);
		collectFaces();
	
		//selectBorders();
		makeSegments();
		collectFaces();
		countVolume();
		
	}
	public void countVolume(){
		int iCells=0;
		for (int x=1;x<gridFine.length-1;x++){
			for (int y=1;y<gridFine[0].length-1;y++){
				for (int z=1;z<gridFine[0][0].length-1;z++){
					Cell cell=gridFine[x][y][z];
					if (cell.getValue()>iso){
						iCells++;
					}
				}
			}
		}
		println("iCells: "+iCells);
		println("vol:: "+(iCells*dimFineCubeX*dimFineCubeY*dimFineCubeZ));
	}
	public void draw() {
		if (mouseX<200){
			cam.setActive(false);
		}
		else{
			cam.setActive(true);
		}
		if (doReset){
			reset();
			doReset=false;
		}
		//innerBounds=new Box3D(sliderX1.getValue(),sliderY1.getValue(),sliderZ1.getValue(),bounds.x2-sliderX1.getValue(),sliderY2.getValue(),sliderZ2.getValue());
		display();
		stroke(0,255,0);
		noFill();
		for (int i=0;i<cubes.size();i++){
			Box3D b=cubes.get(i);
			b.display3D(g);
		}
		display2D();
		//noLoop();
	}
	ArrayList<Box3D> cubes=new ArrayList<Box3D>();
	public void makeSegments(){
		Cell vx=gridFine[0][0][0];
		float dimCubeX=gridFine[1][0][0].x-vx.x;
		float dimCubeY=gridFine[0][1][0].y-vx.y;
		float dimCubeZ=gridFine[0][0][1].z-vx.z;
		float sX=vx.x;
		float sY=vx.y;
		float sZ=vx.z;
		float d=xRange.getLowValue()-bounds.x1;
		xRange.setHighValue(bounds.x2-d);
	Box3D iB=new Box3D();
	iB.x1=xRange.getLowValue();
	iB.y1=yRange.getLowValue();
	iB.z1=zRange.getLowValue();
	iB.x2=bounds.x2-d;
	iB.y2=yRange.getHighValue();
	iB.z2=zRange.getHighValue();
		 x1=(int)((xRange.getLowValue()-sX)/dimCubeX);
		 x2=(int)((xRange.getHighValue()-sX)/dimCubeX);
		 y1=(int)((yRange.getLowValue()-sY)/dimCubeY);
		 y2=(int)((yRange.getHighValue()-sY)/dimCubeY);
		 z1=(int)((zRange.getLowValue()-sZ)/dimCubeZ);
		 z2=(int)((zRange.getHighValue()-sZ)/dimCubeZ);
		 
		// int nY=10;
		 
		 float dZ=zRange.getHighValue()-zRange.getLowValue();
		 int nZ=(int)(dZ/0.4f)+1;
		 float distZ=dZ/nZ;
		for (int z = 1; z < nZ; z++) {
			int zK = (int) ((z * distZ)/ dimCubeZ)+z1;
			for (int y = 0; y < gridFine[0].length; y++) {
				for (int x = 0; x < gridFine.length; x++) {
					Cell v = gridFine[x][y][zK];
					if (!v.off && !v.reached)
						v.setValue(1);
					v = gridFine[x][y][zK + 1];
					if (!v.off && !v.reached)
						v.setValue(1);
				}
			}
		}

		 float dY=yRange.getHighValue()-yRange.getLowValue();
		 int nY=(int)(dY/0.4f)+1;
		 float distY=dY/nY;
		for (int y = 1; y < nY; y++) {
			int yK = (int) ((y * distY)/ dimCubeY)+y1;
			for (int z = 0; z < gridFine[0][0].length; z++) {
				for (int x = 0; x < gridFine.length; x++) {
					Cell v = gridFine[x][yK][z];
					if (!v.off && !v.reached)
						v.setValue(1);
					v = gridFine[x][yK][z];
					if (!v.off && !v.reached)
						v.setValue(1);
				}
			}
		}

		 float dX=xRange.getHighValue()-xRange.getLowValue();
		 int nX=(int)(dX/0.4f)+1;
		 float distX=dX/nX;
		for (int x = 1; x < nX; x++) {
			int xK = (int) ((x * distX)/ dimCubeX)+x1;
			for (int z = 0; z < gridFine[0][0].length; z++) {
				for (int y = 0; y < gridFine[0].length; y++) {
					Cell v = gridFine[xK][y][z];
					if (!v.off && !v.reached)
						v.setValue(1);
					v = gridFine[xK][y][z];
					if (!v.off && !v.reached)
						v.setValue(1);
				}
			}
		}
		cubes=new ArrayList<Box3D>();
		int nFull=0;
		int nEmpty=0;
		for (int x=0;x<nX;x++){
			for (int y=0;y<nY;y++){
				for (int z=0;z<nZ;z++){
					int xK = (int) ((x * distX)/ dimCubeX)+x1;
					int yK = (int) ((y * distY)/ dimCubeY)+y1;
					int zK = (int) ((z * distZ)/ dimCubeZ)+z1;
					int xK2 = (int) (((x+1) * distX)/ dimCubeX)+x1;
					int yK2 = (int) (((y+1) * distY)/ dimCubeY)+y1;
					int zK2 = (int) (((z+1) * distZ)/ dimCubeZ)+z1;
					boolean full=false;
					for (int cX=xK;cX<xK2;cX++){
						for (int cY=yK;cY<yK2;cY++){
							for (int cZ=zK;cZ<zK2;cZ++){
								Cell cell=gridFine[cX][cY][cZ];
								if (cell.reached){
									full=true;
									cX=xK2;
									cY=yK2;
									cZ=zK2;
								}
							}
						}
					}
					if (full){nFull++;
				
					
					}
					else{
						Box3D nBox=new Box3D(x*distX+iB.x1,y*distY+iB.y1,z*distZ+iB.z1,(x+1)*distX+iB.x1,(y+1)*distY+iB.y1,(z+1)*distZ+iB.z1);
						cubes.add(nBox);
						nEmpty++;
					}
				}
			}
		}
		println("nCubes: "+nFull);
		println("nEmpty: "+nEmpty);
	}
	
	public void selectBorders() {
		for (int x = 1; x < gridFine.length - 1; x++) {
			for (int y = 1; y < gridFine[0].length - 1; y++) {
				for (int z = 1; z < gridFine[0][0].length - 1; z++) {
					Cell cell = gridFine[x][y][z];
					cell.border = false;
					if (cell.reached) {
						Cell c = gridFine[x + 1][y][z];
						boolean border = false;

						if (!c.reached &&!c.off)
							border = true;
						c = gridFine[x - 1][y][z];
						if (!c.reached &&!c.off)
							border = true;
						c = gridFine[x][y + 1][z];
						if (!c.reached &&!c.off)
							border = true;
						c = gridFine[x][y - 1][z];
						if (!c.reached &&!c.off)
							border = true;
						 c = gridFine[x][y][z + 1];
						if (!c.reached &&!c.off)
							border = true;
						c = gridFine[x][y][z - 1];
						if (!c.reached &&!c.off)
							border = true;

						if (border) {
							cell.border = true;
						}
					}
				}
			}
		}
		for (int x = 0; x < gridFine.length; x++) {
			for (int y = 0; y < gridFine[0].length; y++) {
				for (int z = 0; z < gridFine[0][0].length; z++) {
					Cell cell = gridFine[x][y][z];
					if (cell.border)
						cell.setValue(1);
					else
						cell.setValue(0);
				}
			}
		}
	}
	public void collectFaces(){
		float dX=dimFineCubeX;
		float dY=dimFineCubeY;
		float dZ=dimFineCubeZ;
		for (int x=1;x<gridFine.length-1;x++){
			for (int y=1;y<gridFine[0].length-1;y++){
				for (int z=1;z<gridFine[0][0].length-1;z++){
					Vec3D[] vs=new Vec3D[8];
					Cell cell=gridFine[x][y][z];
					cell.faces.clear();
					if (cell.getValue()>iso){
					vs[0]=new Vec3D(cell.x,cell.y,cell.z); 
					vs[1]=new Vec3D(cell.x+dX,cell.y,cell.z); 
					vs[2]=new Vec3D(cell.x+dX,cell.y+dY,cell.z); 
					vs[3]=new Vec3D(cell.x,cell.y+dY,cell.z); 
					vs[4]=new Vec3D(cell.x,cell.y,cell.z+dZ); 
					vs[5]=new Vec3D(cell.x+dX,cell.y,cell.z+dZ); 
					vs[6]=new Vec3D(cell.x+dX,cell.y+dY,cell.z+dZ); 
					vs[7]=new Vec3D(cell.x,cell.y+dY,cell.z+dZ); 
					
					Cell cellX=gridFine[x+1][y][z];
					if (cellX.getValue()<iso){
						QuadFace face=new QuadFace(vs[1],vs[2],vs[6],vs[5]);
						cell.faces.add(face);
					}
					 cellX=gridFine[x-1][y][z];
					if (cellX.getValue()<iso){
						QuadFace face=new QuadFace(vs[3],vs[0],vs[4],vs[7]);
						cell.faces.add(face);
					}
					Cell cellY=gridFine[x][y+1][z];
					if (cellY.getValue()<iso){
						QuadFace face=new QuadFace(vs[2],vs[3],vs[7],vs[6]);
						cell.faces.add(face);
					}
					cellY=gridFine[x][y-1][z];
					if (cellY.getValue()<iso){
						QuadFace face=new QuadFace(vs[0],vs[1],vs[5],vs[4]);
						cell.faces.add(face);
					}
					Cell cellZ=gridFine[x][y][z+1];
					if (cellZ.getValue()<iso){
						QuadFace face=new QuadFace(vs[4],vs[5],vs[6],vs[7]);
						cell.faces.add(face);
					}
					cellZ=gridFine[x][y][z-1];
					if (cellZ.getValue()<iso){
						QuadFace face=new QuadFace(vs[3],vs[2],vs[1],vs[0]);
						cell.faces.add(face);
					}
					if (cell.faces.size()>0){
						cell.massive=true;
					}
					}
				}
			}
		}
	}
	
	public void displayFine() {
		fill(255);
		int nX = gridFine.length;
		int nY = gridFine[0].length;
		int nZ = gridFine[0][0].length;
		
		for (int i = 0; i < yButs.length; i++) {
			if (yButs[i].active) {
				int y1 = i * fac;
				int y2 = (i + 1) * fac;
				for (int x = 0; x < nX; x++) {
					for (int y = y1; y < y2; y++) {
						for (int z = 0; z < nZ; z++) {
							Cell cell = gridFine[x][y][z];
							
								if (cell.massive){
									for (int j=0;j<cell.faces.size();j++){
										QuadFace face=cell.faces.get(j);
										face.display3D(g);
									}
								}
								/*pushMatrix();
								translate(cell.x, cell.y, cell.z);
								box(dimFineCubeX, dimFineCubeY, dimFineCubeZ);
								popMatrix();*/
								
							
						}
					}
				}
			}
		}
		
	}
	public void display() {
		background(0,0,255);
		//lights();
		pointLight(220,210,200,2,3,1);
		pointLight(200,220,210,-3,-2,-1);
		rotateX(HALF_PI);
		fill(255);
		noStroke();
		if (mesh != null) {
			for (int i=0;i<mesh.faces.size();i+=50){
				AbstractFace face=mesh.faces.get(i);
				//face.display3D(g);
			}
			
		}
		int iCube=0;
		int maxCubes= grid.length*grid[0].length*grid[0][0].length;
		int maxX=(int)(grid.length*0.5f+1);
		randomSeed(1);
		int fullCubes=0;
		
		for (int x = 0; x < maxX; x++) {
			if (xButs[x].active)
			for (int y = 0; y < grid[0].length; y++) {
				if (yButs[y].active)
				for (int z = 0; z < grid[0][0].length; z++) {
					iCube++;
					Cell Cell = grid[x][y][z];
					int cColor=colors[Cell.type].getRGB();
					if (!Cell.off){
					if (zButs[z].active)
					if (Cell.objects.size() > 0) {
						fullCubes++;
						stroke(255);
						noFill();
						pushMatrix();
						translate(dimCubeX * 0.5f,dimCubeY
								* 0.5f, dimCubeZ * 0.5f);
						translate(Cell.x , Cell.y , Cell.z );
						//box(dimCubeX, dimCubeY, dimCubeZ);
						popMatrix();
						noStroke();
						fill(random(255), 255, 255);
					
						int iX=x*fac;
						int iY=y*fac;
						int iZ=z*fac;
						fill(cColor);
						
						for (int cX=iX;cX<iX+fac;cX++){
							for (int cY=iY;cY<iY+fac;cY++){
								for (int cZ=iZ;cZ<iZ+fac;cZ++){
									Cell smallVox=gridFine[cX][cY][cZ];
									if (smallVox.objects.size()>0&&!smallVox.reached){
										pushMatrix();
										translate(smallVox.x,smallVox.y,smallVox.z );
										//box(dimFineCubeX, dimFineCubeY, dimFineCubeZ);
										popMatrix();
									}
								}
							}
						}
						
					}
					}
				}
			
			}
		}
		
		displayFine();
	}

	public void display2D() {
		hint(DISABLE_DEPTH_TEST);
		colorMode(RGB,255);
		noLights();
		stroke(0);
		fill(0);
		camera();
		cp5.draw();
		for (int i=0;i<xButs.length;i++){
			Button b=xButs[i];
			b.display(g);
		}
		for (int i=0;i<yButs.length;i++){
			Button b=yButs[i];
			b.display(g);
		}
		for (int i=0;i<zButs.length;i++){
			Button b=zButs[i];
			b.display(g);
		}
		bgroup.display(g);
		groupType.display(g);
		int iY=bgroup.getSelected();
		canvas.zK=iY;
		canvas.display(g);
		fill(255,0,0);
		hint(ENABLE_DEPTH_TEST);
	}
	public void scanTriangles(Cell[][][] grid){
		Cell vx=grid[0][0][0];
		float dimCubeX=grid[1][0][0].x-vx.x;
		float dimCubeY=grid[0][1][0].y-vx.y;
		float dimCubeZ=grid[0][0][1].z-vx.z;
		int nX = grid.length;
		int nY = grid[0].length;
		int nZ = grid[0][0].length;
		float sX=vx.x;
		float sY=vx.y;
		float sZ=vx.z;
		for (int i=0;i<mesh.faces.size();i++){
			
			if (i%100000==0)println(i+"/"+mesh.faces.size());
			SimpleTriangle t=(SimpleTriangle)mesh.faces.get(i);
			Box3D b=t.getBounds3D();
			
			for (int j=0;j<t.getNodesNum();j++){
				HasPos3D v=t.getNode(j);
			
			}
			//Vec3D b=t.getGravityCenter();
			
			int gridX1=(int)((b.x1-sX)/dimCubeX);
			int gridY1=(int)((b.y1-sY)/dimCubeY);
			int gridZ1=(int)((b.z1-sZ)/dimCubeZ);
			int gridX2=(int)((b.x2-sX)/dimCubeX);
			int gridY2=(int)((b.y2-sY)/dimCubeY);
			int gridZ2=(int)((b.z2-sZ)/dimCubeZ);
			gridX2=min(gridX2,nX-1);
			gridY2=min(gridY2,nY-1);
			gridZ2=min(gridZ2,nZ-1);
			for (int x=gridX1;x<=gridX2;x++){
				for (int y=gridY1;y<=gridY2;y++){
					for (int z=gridZ1;z<=gridZ2;z++){
						Cell vox=grid[x][y][z];
						vox.check=true;
						//float dX=vox.
						vox.setValue(1);
						vox.add(t);
					}
				}
			
			}
		}
	}
	public void findOutline(Cell[][][] grid,int cX, int cY, int cZ) {
		int nX=grid.length;
		int nY=grid[0].length;
		int nZ=grid[0][0].length;
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					cell.reached = false;
				}
			}
		}
		Vector<Cell> candidates = new Vector<Cell>();
		candidates.add(grid[cX][cY][cZ]);
		for (int i = 0; i < candidates.size(); i++) {
			Cell cell = candidates.get(i);
			cell.reached = true;
		}
	
		while (candidates.size() > 0) {
			Vector<Cell> nextCandidates = new Vector<Cell>();
			for (int i = 0; i < candidates.size(); i++) {
				Cell cell = candidates.get(i);
	
				int sX = max(cell.getXK() - 1, 0);
				int sY = max(cell.getYK() - 1, 0);
				int sZ = max(cell.getZK() - 1, 0);
	
				int eX = min(cell.getXK() + 2, nX);
				int eY = min(cell.getYK() + 2, nY);
				int eZ = min(cell.getZK() + 2, nZ);
	
				for (int x = sX; x < eX; x++) {
					for (int y = sY; y < eY; y++) {
						for (int z = sZ; z < eZ; z++) {
							Cell nb = grid[x][y][z];
							if (!nb.reached && nb.getValue() < iso&&!nb.off) {
								nb.reached = true;
								nextCandidates.add(nb);
							}
						}
					}
				}
			}
			candidates = nextCandidates;
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.reached &&!cell.off) {
						cell.setValue(1);// 0
					}
					else{
						cell.setValue(0);
					}
				}
			}
		}
	}
	public int roundUp(int n,int a){
		if (n%a==0)return n;
		return ((int)(n/a)+1)*a;
	}
	public Cell[][][] voxelize(float dimCubeX,float dimCubeY,float dimCubeZ){
		Bounds3D b3d=mesh.getBounds3D();
		float cX=b3d.getCenterX();
		float cY=b3d.getCenterY();
		float cZ=b3d.getCenterZ();
		int nX=(int)(b3d.getDimX()*0.5f/dimCubeX)+1;
		nX=roundUp(nX,8);
		int nY=(int)(b3d.getDimY()*0.5f/dimCubeY)+1;
		nY=roundUp(nY,8);
		int nZ=(int)(b3d.getDimZ()*0.5f/dimCubeZ)+1;
		nZ=roundUp(nZ,8);
		
		sX=cX-nX*dimCubeX;
		sY=cY-nY*dimCubeY;
		sZ=cZ-nZ*dimCubeZ;
		
		nX=2*nX;
		nY=2*nY;
		nZ=2*nZ;
		bounds=new Box3D(sX,sY,sZ,sX+nX*dimCubeX,sY+nY*dimCubeY,sZ+nZ*dimCubeZ);
		grid=new Cell[nX][nY][nZ];
		for (int x=0;x<grid.length;x++){
			for (int y=0;y<grid[0].length;y++){
				for (int z=0;z<grid[0][0].length;z++){
					Cell cell=new Cell(x*dimCubeX+sX,y*dimCubeY+sY,z*dimCubeZ+sZ);
					cell.xK=(short)x;
					cell.yK=(short)y;
					cell.zK=(short)z;
					grid[x][y][z]=cell;
				}
			}
		}
		println();
		scanTriangles(grid);
		return grid;
	}
	public void exportFaces(String file,Cell[][][]grid){
		PrintWriter out=this.createWriter(file);
		int iV=1;
		for (int x=0;x<grid.length;x++){
			for (int y=0;y<grid[0].length;y++){
				for (int z=0;z<grid[0][0].length;z++){
					Cell cell=grid[x][y][z];
					for (int i=0;i<cell.faces.size();i++){
						QuadFace face=cell.faces.get(i);
						for (int j=0;j<face.getNodesNum();j++){
							HasPos3D n=face.getNode(j);
							out.println("v "+n.getX()+" "+n.getY()+" "+n.getZ());
						}
						out.println("f "+(iV+0)+" "+(iV+1)+" "+(iV+2)+" "+(iV+3));
						iV+=4;
					}
				}
			}
		}
		out.flush();
		out.close();
		println("saved");
	}
	public void export(String file,Cell[][][]grid){
		PrintWriter out=this.createWriter(file);
		int iV=1;
		int iC=0;
		for (int x=0;x<grid.length;x++){
			for (int y=0;y<grid[0].length;y++){
				for (int z=0;z<grid[0][0].length;z++){
					Cell cell=grid[x][y][z];
					if (cell.objects.size()>0){
						iC++;
						Vec3D[]vertices=new Vec3D[8];
						vertices[0]=new Vec3D(cell.x,cell.y,cell.z); 
						vertices[1]=new Vec3D(cell.x+dimCubeX,cell.y,cell.z); 
						vertices[2]=new Vec3D(cell.x+dimCubeX,cell.y+dimCubeY,cell.z); 
						vertices[3]=new Vec3D(cell.x,cell.y+dimCubeY,cell.z); 
						vertices[4]=new Vec3D(cell.x,cell.y,cell.z+dimCubeZ); 
						vertices[5]=new Vec3D(cell.x+dimCubeX,cell.y,cell.z+dimCubeZ); 
						vertices[6]=new Vec3D(cell.x+dimCubeX,cell.y+dimCubeY,cell.z+dimCubeZ); 
						vertices[7]=new Vec3D(cell.x,cell.y+dimCubeY,cell.z+dimCubeZ); 
						for (int i=0;i<vertices.length;i++){
							Vec3D v=vertices[i];
							
							out.println("v "+v.x+" "+v.y+" "+v.z);
						}
						out.println("g "+iC);
						out.println("f "+(iV+3)+" "+(iV+2)+" "+(iV+1)+" "+(iV+0));
						out.println("f "+(iV+4)+" "+(iV+5)+" "+(iV+6)+" "+(iV+7));
						out.println("f "+(iV+0)+" "+(iV+1)+" "+(iV+5)+" "+(iV+4));
						out.println("f "+(iV+1)+" "+(iV+2)+" "+(iV+6)+" "+(iV+5));
						out.println("f "+(iV+2)+" "+(iV+3)+" "+(iV+7)+" "+(iV+6));
						out.println("f "+(iV+3)+" "+(iV+0)+" "+(iV+4)+" "+(iV+7));
						iV+=8;
					}
				}
			}
		}
		out.flush();
		out.close();
		println("saved");
	}
	public void loadDistance(String file) {
		String[] lines = this.loadStrings(file);
		String cString = lines[0];
		String[] strings = cString.split(" ");
		int nX = Integer.valueOf(strings[0]);
		int nY = Integer.valueOf(strings[1]);
		int nZ = Integer.valueOf(strings[2]);
		cString = lines[1];
		strings = cString.split(" ");
		float x1=Float.valueOf(strings[0]);
		float y1=Float.valueOf(strings[1]);
		float z1=Float.valueOf(strings[2]);
		float x2=Float.valueOf(strings[3]);
		float y2=Float.valueOf(strings[4]);
		float z2=Float.valueOf(strings[5]);
		bounds=new Box3D(x1,y1,z1,x2,y2,z2);
		dimFineCubeX=bounds.getDimX()/nX;
		dimFineCubeY=bounds.getDimY()/nY;
		dimFineCubeZ=bounds.getDimZ()/nZ;
		gridFine = new Cell[nX][nY][nZ];
		
		for (int i = 2; i < lines.length; i++) {
			String l = lines[i];
			int j = i - 2;
			int cX = ((int) (j * 1f / (nZ * nY)));
			int cY = (int) (j * 1f / nZ) % nY;
			int cZ = j % (nZ);
			double d = Double.valueOf(l);
			Cell cell=new Cell(bounds.x1+cX*dimFineCubeX,bounds.y1+cY*dimFineCubeY,bounds.z1+cZ*dimFineCubeZ);
			cell.xK=(short)cX;
			cell.yK=(short)cY;
			cell.zK=(short)cZ;
			cell.setValue((float) d);
			gridFine[cX][cY][cZ]=cell;
			
		}
		println("loaded");
	}

	public void saveDistance(String file) {
		try {
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			PrintStream printstream = new PrintStream(fileOutputStream);
			printstream.println(gridFine.length+" "+gridFine[0].length+" "+gridFine[0][0].length);
			printstream.println(bounds.x1+" "+bounds.y1+" "+bounds.z1+" "+bounds.x2+" "+bounds.y2+" "+bounds.z2);
			for (int x = 0; x < gridFine.length; x++) {
				println("x"+x+"/"+gridFine.length);
				for (int y = 0; y < gridFine[x].length; y++) {
					for (int z = 0; z < gridFine[x][y].length; z++) {
						Cell c = gridFine[x][y][z];
						printstream.println("" + c.getValue());
					}
				}
			}
			printstream.close();
			fileOutputStream.close();
		} catch (Exception e) {
			System.err.println("error writing file");
		}
		println("distance saved");
	}

	public void keyPressed(){
		if (key=='r'){
			doReset=true;
		}
	}
	class GridCanvas extends Rect2D implements MouseListener,MouseMotionListener{
		int dim;
		int zK;
		GridCanvas(float x,float y,int dim){
			super(x,y,x+dim*grid.length,y+dim*grid[0][0].length);
			this.dim=dim;
		}
		public Cell getCell(int x,int y){
			if (contains(x,y)){
				int xK=(int)((x-x1)/dim);
				int yK=(int)((y-y1)/dim);
				return grid[xK][zK][yK];
			}
			return null;
		}
		public void setTypeToCells(int x,int y,int type){
			if (contains(x,y)){
				int xK=(int)((x-x1)/dim);
				int yK=(int)((y-y1)/dim);
				for (int cY=0;cY<grid[0].length;cY++){
					grid[xK][cY][yK].type=type;
				}
			}
		}
		
		public void display(PGraphics g){
			
			for (int x=0;x<grid.length;x++){
				for (int z=0;z<grid[0][0].length;z++){
					Cell vox=grid[x][zK][z];
					if (vox.off) fill(50);
					else if (vox.massive)fill(colors[vox.type].getRGB());
					else fill(255);
					float cX=x*dim+x1;
					float cY=z*dim+y1;
					rect(cX,cY,dim,dim);
					
					if (vox.objects.size()>0) fill(0);
					rect(cX+2,cY+2,dim-4,dim-4);
				}
			}
			stroke(0,255,0);
			noFill();
			super.display(g);
		}
		@Override
		public void mouseClicked(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		@Override
		public void mouseEntered(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		@Override
		public void mouseExited(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		@Override
		public void mousePressed(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		@Override
		public void mouseReleased(MouseEvent e) {
			/*if (contains(e.getX(),e.getY())){
				Cell vox=getCell(e.getX(),e.getY());
				setTypeToCells(e.getX(),e.getY(),groupType.getSelected());
				//vox.type=groupType.getSelected();
				float cX=getCenterX();
				float dX=e.getX()-cX;
				setTypeToCells((int)(cX-dX),e.getY(),groupType.getSelected());
				 //vox=getCell((int)(cX-dX),e.getY());
				//vox.type=groupType.getSelected();
			}*/
			// TODO Auto-generated method stub
		}
		@Override
		public void mouseDragged(MouseEvent e) {
			// TODO Auto-generated method stub
			if (contains(e.getX(),e.getY())){
				Cell vox=getCell(e.getX(),e.getY());
				setTypeToCells(e.getX(),e.getY(),groupType.getSelected());
				//vox.type=groupType.getSelected();
				float cX=getCenterX();
				float dX=e.getX()-cX;
				setTypeToCells((int)(cX-dX),e.getY(),groupType.getSelected());
				 //vox=getCell((int)(cX-dX),e.getY());
				//vox.type=groupType.getSelected();
			}
		}
		@Override
		public void mouseMoved(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}
	}
	class ButtonGroup {
		ArrayList<Button> buttons = new ArrayList<Button>();
		boolean radio;
		public void add(Button btn) {
			btn.parent = this;
			buttons.add(btn);
		}
		public void display(PGraphics g){
			for (int i=0;i<buttons.size();i++){
				Button button=buttons.get(i);
				button.display(g);
			}
		}
		public int getSelected(){
			for (int i = 0; i < buttons.size(); i++) {
				Button btn=buttons.get(i);
				if (btn.isActive())return i;
			}
			return 0;
		}
		public void setRadioActive(Button button) {
			if (radio) {
				for (int i = 0; i < buttons.size(); i++) {
					buttons.get(i).active = false;
					if (buttons.get(i) == button) {
						buttons.get(i).active = true;
					}
				}
			} else {
				button.active = true;
			}
		}
	}
	class Button extends  Rect2D implements MouseListener{
		
		ButtonGroup parent;
		Color color=Color.LIGHT_GRAY;
		int id;

		public Button(float x1, float y1, float x2, float y2) {
			super(x1, y1, x2, y2);
			// TODO Auto-generated constructor stub
		}

		public void display(PGraphics g){
			noStroke();
			if (active)fill(255,0,0);
			else fill(255);
			super.display(g);
			fill(color.getRGB());
			rect(x1+2,y1+2,this.getDimX()-4,this.getDimY()-4);
		}

		boolean active;

		public boolean isActive() {
			return active;
		}

		public void setActive(boolean active) {
			this.active = active;
		}

		@Override
		public void mouseClicked(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
			if (contains(arg0.getX(),arg0.getY())){
				if (parent!=null&&parent.radio){
					parent.setRadioActive(this);
				}
				else{
				active=!active;
				}
			}
		}

		@Override
		public void mouseEntered(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mousePressed(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseReleased(MouseEvent arg0) {
			// TODO Auto-generated method stub
		}
	}
	
	class Cell{
		float x;
		float y;
		float z;
		public short xK;
		public short yK;
		public short zK;
		boolean reached;
		boolean off;
		int type=0;
		boolean check;
		boolean massive;
		ArrayList<QuadFace> faces=new ArrayList<QuadFace>();
		float value;
		boolean border;
		public float getValue() {
			return value;
		}
		public void setValue(float value) {
			this.value = value;
		}
		
		public boolean isOff() {
			return off;
		}
		public void setOff(boolean off) {
			this.off = off;
		}
		
		ArrayList<SimpleTriangle>objects=new ArrayList<SimpleTriangle>();
		Cell(float x, float y, float z) {
			this.x = x;
			this.y = y;
			this.z = z;
		}
		public void add(SimpleTriangle t){
			objects.add(t);
		}
		public int getXK() {
			// TODO Auto-generated method stub
			// return nX%3;
			return xK;
			// return x;
		}

		public int getYK() {
			// TODO Auto-generated method stub
			return yK;
			// return y;
		}

		public int getZK() {
			// TODO Auto-generated method stub
			return zK;
			// return z;
		}

	}

}
